package org.daochong.lang.security;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import org.daochong.lang.StringUtils;

public class SSHCertificateUtils {

	private static final byte[] BEGIN = "-----BEGIN RSA PRIVATE KEY-----".getBytes();
	private static final byte[] END = "-----END RSA PRIVATE KEY-----".getBytes();

	public static void writePub(OutputStream out, String username, String domain, RSAPublicKey publicKey)
			throws IOException {
		out.write("ssh-rsa ".getBytes());
		BigInteger bi = publicKey.getPublicExponent();
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		byte[] tmp = bi.toByteArray();
		bout.write(new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' });
		bout.write(StringUtils.decodeToBytes(tmp.length));
		bout.write(tmp);
		bi = publicKey.getModulus();
		tmp = bi.toByteArray();
		bout.write(StringUtils.decodeToBytes(tmp.length));
		bout.write(bi.toByteArray());
		tmp = bout.toByteArray();
		bout.close();
		out.write(StringUtils.base64Encode(tmp).replaceAll("\\r\\n", "").getBytes());
		if (username != null && domain != null) {
			out.write((" " + username + "@" + domain).getBytes());
		}
		out.close();
	}

	public static void writePrivate(OutputStream out, RSAPrivateKey privateKey, String password) throws IOException {
		RSAPrivateCrtKey key = (RSAPrivateCrtKey) privateKey;
		byte[] n_array = new BigInteger(String.valueOf(key.getModulus().toString(2).length()), 10).toByteArray();
		byte[] prv_array = key.getPrivateExponent().toByteArray();
		byte[] pub_array = key.getPublicExponent().toByteArray();
		byte[] p_array = key.getPrimeP().toByteArray();
		byte[] q_array = key.getPrimeQ().toByteArray();
		byte[] ep_array = key.getPrimeExponentP().toByteArray();
		byte[] eq_array = key.getPrimeExponentQ().toByteArray();
		byte[] c_array = key.getCrtCoefficient().toByteArray();
		int content = 1 + countLength(1) + 1 + // INTEGER
				1 + countLength(n_array.length) + n_array.length + // INTEGER N
				1 + countLength(pub_array.length) + pub_array.length + // INTEGER
																		// pub
		1 + countLength(prv_array.length) + prv_array.length + // INTEGER
																// prv
		1 + countLength(p_array.length) + p_array.length + // INTEGER p
				1 + countLength(q_array.length) + q_array.length + // INTEGER q
				1 + countLength(ep_array.length) + ep_array.length + // INTEGER
																		// ep
		1 + countLength(eq_array.length) + eq_array.length + // INTEGER
																// eq
		1 + countLength(c_array.length) + c_array.length; // INTEGER c

		int total = 1 + countLength(content) + content; // SEQUENCE

		byte[] plain = new byte[total];
		int index = 0;
		index = writeSEQUENCE(plain, index, content);
		index = writeINTEGER(plain, index, new byte[1]); // 0
		index = writeINTEGER(plain, index, n_array);
		index = writeINTEGER(plain, index, pub_array);
		index = writeINTEGER(plain, index, prv_array);
		index = writeINTEGER(plain, index, p_array);
		index = writeINTEGER(plain, index, q_array);
		index = writeINTEGER(plain, index, ep_array);
		index = writeINTEGER(plain, index, eq_array);
		index = writeINTEGER(plain, index, c_array);

		byte[] passphrase = null;
		if (!StringUtils.isEmpty(password)) {
			passphrase = password.getBytes("UTF-8");
		}

		byte[][] _iv = new byte[1][];
		byte[] cr = "\r\n".getBytes();
		byte[] encoded = encrypt(plain, _iv, passphrase);
		if (encoded != plain)
			bzero(plain);
		byte[] iv = _iv[0];
		byte[] prv = StringUtils.base64Encode(encoded).getBytes();

		try {
			out.write(BEGIN);
			out.write(cr);
			if (passphrase != null) {
				//out.write("Proc-Type: 4,ENCRYPTED".getBytes());
				//out.write(cr);
				//out.write("DEK-Info: AES-128-CBC,CE77440232E4E6CFE9C77CC2D3B0504F".getBytes());
				//for (int i = 0; i < iv.length; i++) {
				//	out.write(b2a((byte) ((iv[i] >>> 4) & 0x0f)));
				//	out.write(b2a((byte) (iv[i] & 0x0f)));
				//}
				//out.write(cr);
				//out.write(cr);
			}
			out.write(prv);
			out.write(cr);
			out.write(END);
			out.write(cr);
			out.close();
		} catch (Exception e) {
		}
	}

	private static byte[] encrypt(byte[] plain, byte[][] _iv, byte[] passphrase) {
		if (passphrase == null)
			return plain;
		return plain;

	}

	static private byte a2b(byte c) {
		if ('0' <= c && c <= '9')
			return (byte) (c - '0');
		return (byte) (c - 'a' + 10);
	}

	static private byte b2a(byte c) {
		if (0 <= c && c <= 9)
			return (byte) (c + '0');
		return (byte) (c - 10 + 'A');
	}

	static void bzero(byte[] foo) {
		if (foo == null)
			return;
		for (int i = 0; i < foo.length; i++)
			foo[i] = 0;
	}

	private static int countLength(int len) {
		int i = 1;
		if (len <= 0x7f)
			return i;
		while (len > 0) {
			len >>>= 8;
			i++;
		}
		return i;
	}

	public static int writeSEQUENCE(byte[] buf, int index, int len) {
		buf[index++] = 0x30;
		index = writeLength(buf, index, len);
		return index;
	}

	public static int writeINTEGER(byte[] buf, int index, byte[] data) {
		buf[index++] = 0x02;
		index = writeLength(buf, index, data.length);
		System.arraycopy(data, 0, buf, index, data.length);
		index += data.length;
		return index;
	}

	public static int writeLength(byte[] data, int index, int len) {
		int i = countLength(len) - 1;
		if (i == 0) {
			data[index++] = (byte) len;
			return index;
		}
		data[index++] = (byte) (0x80 | i);
		int j = index + i;
		while (i > 0) {
			data[index + i - 1] = (byte) (len & 0xff);
			len >>>= 8;
			i--;
		}
		return j;
	}

}
